#include "redsocks.h"

// Loader
extern "C"
void
load_module ()
{
  addToSystem ("new-socket", dNewSocket);
  addToSystem ("connect", dConnect);
  addToSystem ("listen", dListen);
  addToSystem ("accept", dAccept);
  addToSystem ("close-socket", dDisconnect);
  addToSystem ("send", dSend);
  addToSystem ("recv", dRecv);
}

dSocket::dSocket ()
{
  type = 'z';
  typeStr = "SOCKET";
  sock = new TCPSocket ();
}

dSocket::dSocket (TCPSocket *socket)
{
  type = 'z';
  typeStr = "SOCKET";
  sock = socket;
}

dSocket::~dSocket ()
{
  if (sock != 0)
    delete sock;
}

string
dSocket::toString ()
{
  return typeStr;
}

dObject *
dNewSocket (dList *args)
{
  return new dSocket ();
}

dObject *
dConnect (dList *args)
{
  // (open-socket socket ip-address port) => boolean
  if (args == 0 || args->size > 3 || (*args)[0]->typeStr != "SOCKET")
    return new dError ("open-socket requires a socket and accepts a string and an integer");
  string ip = "127.0.0.1";
  int port = 80;
  if ((*args)[1] != nil)
    {
      if ((*args)[1]->type != 's')
	return new dError ("open-socket requires a socket and accepts a string and an integer");
      else
	ip = ((dString*)(*args)[1])->value;
    }
  if ((*args)[2] != nil)
    {
      if ((*args)[2]->type != 'i')
	return new dError ("open-socket requires a socket and accepts a string and an integer");
      else
	port = toInt ((dNumber*)(*args)[2]);
    }
  dSocket *socket = (dSocket*)(*args)[0];
  if (socket->sock->Connect (ip.c_str (), port) == 0)
    return T;
  return nil;
}

dObject *
dListen (dList *args)
{
  // (listen socket port) => boolean
  if (args == 0 || args->size > 2 || (*args)[0]->typeStr != "SOCKET")
    return new dError ("listen requires a socket and accepts an integer");
  int port = 80;
  if ((*args)[1] != nil)
    {
      if ((*args)[1]->type != 'i')
	return new dError ("listen requires a socket and accepts an integer");
      else
	port = toInt ((dNumber*)(*args)[1]);
    }
  dSocket *socket = (dSocket*)(*args)[0];
  if (socket->sock->Listen (port) == CC_ERR_NONE)
    return T;
  return nil;
}

dObject *
dAccept (dList *args)
{
  // (accept-socket socket) => socket
  if (args == 0 || args->size != 1 || (*args)[0]->typeStr != "SOCKET")
    return new dError ("accept-socket requires a socket");
  dSocket *newSocket;
  dSocket *socket = (dSocket*)(*args)[0];
  TCPSocket *temp = socket->sock->Accept ();
  if (temp != NULL)
    {
      newSocket = new dSocket (temp);
      return newSocket;
    }
  return nil;
}

dObject *
dDisconnect (dList *args)
{
  // (close-socket socket) => boolean
  if (args == 0 || args->size != 1 || (*args)[0]->typeStr != "SOCKET")
    return new dError ("close-socket requires a socket");
  dSocket *socket = (dSocket*)(*args)[0];
  if (socket->sock->Close () == 0)
    return T;
  return nil;
}

dObject *
dSend (dList *args)
{
  // (send socket "message") => integer
  if (args == 0 || args->size != 2
      || (*args)[0]->typeStr != "SOCKET" || (*args)[1]->type != 's')
    return new dError ("send-socket requires a socket and string");
  dSocket *socket = (dSocket*)(*args)[0];
  string msg = ((dString*)(*args)[1])->value;
  return new dNumber (socket->sock->Send (msg));
}

dObject *
dRecv (dList *args)
{
  // (recv socket) => string
  if (args == 0 || args->size != 1 || (*args)[0]->typeStr != "SOCKET")
    return new dError ("recv requires a socket");
  dSocket *socket = (dSocket*)(*args)[0];
  string msg;
  if (socket->sock->Read (msg) == 0)
    return new dString (msg);
  return nil;
}
